#!/usr/bin/python

import signal;
import gdb;

real_read_clock = 0;
read_clock = 0;
read_num = 0;
read_comm = {};

def tfind_entry(num):
	if num < 0:
		tid = gdb.execute("tfind", False, True);
	else:
		tid = gdb.execute("tfind "+str(num), False, True);
	if tid.find(", tracepoint ") < 0:
		return -1;
	tid = tid[tid.find(", tracepoint ") + len(", tracepoint "):];
	return int(tid);

def print_top(comm, index, num):
	for e in comm:
		print e[0], e[1][index];
		num -= 1;
		if num <= 0:
			break;

def sigint_handler(num, e):
	print "vfs_read: ", read_clock, "ns", read_num, "times";
	print "";
	print "Top 3 of clock is:";
	print_top (sorted(read_comm.items(), key=lambda d:d[1][0], reverse=True), 0, 3);
	print "";
	print "Top 3 of times is:";
	print_top (sorted(read_comm.items(), key=lambda d:d[1][1], reverse=True), 1, 3);
	print "";
	print "Top 3 of clock per times is:";
	print_top (sorted(read_comm.items(), key=lambda d:d[1][2], reverse=True), 2, 3);
	print "";
	print "Top 3 of real clock is:";
	print_top (sorted(read_comm.items(), key=lambda d:d[1][3], reverse=True), 3, 3);
	print "";
	print "Top 3 of real clock per times is:";
	print_top (sorted(read_comm.items(), key=lambda d:d[1][4], reverse=True), 4, 3);

	try:
		s = raw_input('Continue? (yes)')
	except:
		s = 'y'
	finally:
		if s[0:1] != 'n' and s[0:1] != 'N':
			return;
	exit(1);

gdb.execute("set pagination off", True, True);

#set tracepoint id
vfs_read_begin = 1;
vfs_read_end = 2;
schedule = 3;

signal.signal(signal.SIGINT, sigint_handler);
signal.siginterrupt(signal.SIGINT, False);

#connect to the gdbframe_pipe
gdb.execute("list vfs_read");
gdb.execute("target tfile /sys/kernel/debug/gtpframe_pipe");

r_list = {};

while 1:
	try:
		tid = tfind_entry(-1);
		if tid < 0:
			raise gdb.error ("Something wrong, drop one entry.");

		if tid == vfs_read_begin:
			entry = {};
			entry["begin_clock"] = long(gdb.parse_and_eval("$clock"));
			entry["prev_clock"] = entry["begin_clock"];
			entry["real_clock"] = 0;
			pid = long(gdb.parse_and_eval("((struct task_struct *)$current_task)->pid"));
			r_list[pid] = entry;
		elif tid == schedule:
			pid = long(gdb.parse_and_eval("prev->pid"));
			if pid in r_list:
				clock = long(gdb.parse_and_eval("$clock"));
				r_list[pid]["real_clock"] += clock - r_list[pid]["prev_clock"];
				r_list[pid]["prev_clock"] = clock;
			else:
				pid = long(gdb.parse_and_eval("next->pid"));
				if pid in r_list:
					r_list[pid]["prev_clock"] = long(gdb.parse_and_eval("$clock"));
		elif tid == vfs_read_end:
			pid = long(gdb.parse_and_eval("((struct task_struct *)$current_task)->pid"));
			if pid not in r_list:
				raise gdb.error ("cannot find vfs_read_begin for vfs_read_end");

			clock = long(gdb.parse_and_eval("$clock"));
			r_list[pid]["real_clock"] += clock - r_list[pid]["prev_clock"];
			clock = clock - r_list[pid]["begin_clock"];

			read_clock += clock;
			real_read_clock += r_list[pid]["real_clock"];
			read_num += 1;

			comm = str(gdb.parse_and_eval("((struct task_struct *)$current_task)->comm"));
			if comm in read_comm:
				read_comm[comm][0] += clock;
				read_comm[comm][1] += 1;
				read_comm[comm][2] = read_comm[comm][0] / read_comm[comm][1];
				read_comm[comm][3] += r_list[pid]["real_clock"];
				read_comm[comm][4] = r_list[pid]["real_clock"] / read_comm[comm][1];
			else:
				read_comm[comm] = [clock, 1, clock, r_list[pid]["real_clock"], r_list[pid]["real_clock"]];
			del(r_list[pid]);
		else:
			raise gdb.error ("Trace id error.");
	except gdb.error, x:
		print("Drop one entry because", x);
	except gdb.MemoryError, x:
		print("Drop one entry because", x); 

	gdb.execute("tfind", False, True);
